#encoding:utf8 
import json, os, sys, logging, time
from tornado import websocket
from tornado import ioloop
logging.basicConfig(level=logging.INFO)

from functools import wraps

def chk_wscon(method):
    @wraps(method)
    def wrapper(self, *argv, **kwds):
        if self._con_status != self.kWsStatus["connected"] or self._wscon == None:
            logging.error("WS connected status is not connected!")
        else:
            return method(self, *argv, **kwds)
    return wrapper

class WebsocketCli:

    DEFAULT_CON_TIMEOUT = 10    #first con timeout
    DEFAULT_REQ_TIMEOUT = 2    #request timeout

    HEARTBEAT_TIME = 2          #heartbeat interval time

    RECON_INTERVAL = 3          #recon interval time

    kWsStatus = dict(
        disconnected = 0, 
        connecting = 1,
        connected = 2
    )


    def __init__(self, url, ioloop, auto_reconnect=True):
        self._wscon = None
        self._url = url
        self._autorecon = auto_reconnect
        self._con_status = self.kWsStatus["disconnected"]
        self._ioloop = ioloop
        
        self._pingval = 0       #now ping value
        self._pstime = 0        #ping start time

        self._recon_nowtimes = 0     
        self.wscon()            #init 

    def wscon(self):
        if self._wscon == None:
            logging.info ("websocket start connect to {url} now..".format(url=self._url))
            self._con_status = self.kWsStatus["connecting"]
            websocket.websocket_connect(self._url, io_loop = self._ioloop, \
                                callback=self._on_concallback, connect_timeout=self.DEFAULT_CON_TIMEOUT, on_message_callback=self._on_message)
        return self._wscon


    @chk_wscon
    def send(self, data):
        msg = ""
        if isinstance(data, dict) or isinstance(data, list):
            msg = json.dumps(data)
        else:
            msg = json.dumps(dict(ret=data))
        logging.info("Cli send info -->{msg}".format(msg=msg))
        self.wscon().write_message(msg)


    @chk_wscon
    def ping(self):
        self._pstime = time.time()
        self._wscon.get_websocket_protocol().write_ping("ping")
    

    def _heart_beat(self):
        if (self.is_connected() == True):
            self.ping()
            self._ioloop.call_later(self.HEARTBEAT_TIME, self._heart_beat)


    def _on_concallback(self, future):
        if future.exception() is None:
            logging.info ("Connect successful!")
            self._con_status = self.kWsStatus["connected"]
            self._wscon = future.result()            
            self._wscon.on_pong = self._on_pong

            self.on_consuccess()
            self._heart_beat()
        else:
            self._close(future.exception())

            
    def _on_message(self, rep):
        logging.info("websocket get msg-->>>%s", rep)
        if rep is None:
            self._close()
        else:
            self._recon_nowtimes=0
            self.on_message(json.loads(rep))
    

    def _on_pong(self, data):
        pval = str(int(float("%.02f"%(time.time() - self._pstime)) * 100)) + "ms"
        self._pstime = 0
        # logging.info("now ping value is {pval}".format(pval=pval))
        self.on_pong(pval)

    def _close(self, reason = ""):
        logging.info ("The ws client connect closed, reson is {reason}".format(reason=reason))
        self._con_status = self.kWsStatus["disconnected"]
        
        self._wscon = None 
        self.on_confaild()

        if self._autorecon == True:   
            self._reconnect()
        else:
            os.exit()


    def _reconnect(self):
        if (self.is_connected() == False):
            self._recon_nowtimes += 1
            self._ioloop.call_later(self.RECON_INTERVAL, self.wscon) 
            logging.info("now reconnect times is -->{times}".format(times=self._recon_nowtimes))

    #===========================================================

    def is_connected(self):
        return self._con_status == self.kWsStatus["connected"]

    def on_consuccess(self):
        # logging.info ("websocket con success!")
        pass
    
    def on_confaild(self):
        # logging.info("websocket con faild!")
        pass

    def on_message(self, msg):
        pass
    
    def on_pong(self, pingval):
        pass





























#======================================================================
#------------------cs
'''
    msg = {
        act = ''
        base = {
            mac = todo,         --mac address 
            ip = todo,          --ip 
        }
        cs = {
            val = if exits! || 0     --control txt         
        }
    }
'''

#------------------sc
'''
    msg = {
        act = ''
        base = {
            mac = todo,         --mac address 
            ip = todo,          --ip 
        }
        sc = {
            r = 0
        }
    }
'''
#======================================================================
class PiCli(WebsocketCli):
    def __init__(self, *argv, **kwads):
        WebsocketCli.__init__(self , *argv, **kwads)
        self._handlers = dict()

    def set_handlers(self, handlers):
        self._handlers = handlers
        
    def handle(self, handlename, kwds = dict()):
        
        if self._handlers.has_key(handlename):
            #todo chkk base!
            #todo req creater!
            #template ret 
            msg = dict(
                act = "light",
                base = dict(
                    mac = "",
                    ip = ""
                ),
                cs = self._handlers[handlename](kwds) 
            )
            self.send(msg)

    def on_pong(self, pingval):
        pass

    def on_message(self, msg):
        pass

    def on_consuccess(self):
        self.handle("light") 

if __name__ == "__main__":
    ioloop = ioloop.IOLoop.current()
    ws_cli = PiCli(ioloop=ioloop, url="ws://127.0.0.1:8089/pi/piapi/dwbtest")

    #act handlers  
    handlers = dict(
        light = lambda rep:1
    )
    ws_cli.set_handlers(handlers)

    ioloop.start()